#include "filetransferhelper.h"

#include <QBuffer>

FiletransferHelper::FiletransferHelper(QObject *parent)
    : QObject{parent}
{

}

FiletransferHelper::~FiletransferHelper()
{

}

//创建连接
void FiletransferHelper::createConnection(QString wurl, QString transferToken)
{
    this->m_isbusy = true;
    this->m_wurl = wurl;
    this->transferToken = transferToken;
    this->m_wsocket = new QWebSocket(this->m_wurl, QWebSocketProtocol::VersionLatest, this);

    connect(m_wsocket, SIGNAL(connected()),
            this, SLOT(slotConnected()));
    connect(m_wsocket, SIGNAL(disconnected()),
            this, SLOT(slotDisconnected()));
    connect(m_wsocket, SIGNAL(binaryMessageReceived(QByteArray)),
            this, SLOT(slotBinaryDataReceived(QByteArray)));
    connect(m_wsocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(slotError(QAbstractSocket::SocketError)));
}

//连接到服务器
void FiletransferHelper::connectToServer()
{
    this->m_isbusy = true;
    this->m_wsocket->open(this->m_wurl);
}

//重新设置传输token，将该连接与另外一个传输批次绑定
void FiletransferHelper::resetTransferToken(QString transferToken)
{
    this->m_isbusy = true;
    this->m_isReady = false;
    this->currentFileToken = "";
    this->localFileName = "";

    this->transferToken = transferToken;
    if(this->m_isConnected == false)
    {
        this->m_wsocket->close();
    }
    this->m_wsocket->open(this->m_wurl);
}

//下载文件
void FiletransferHelper::downloadFile(QString fileToken, QString localFullName, long fileLength)
{
    this->m_isbusy = true;

    //记录文件大小
    this->fileLength = fileLength;

    QFileInfo qinfo(localFullName);
    QDir dir = qinfo.absoluteDir();

    //如果目录不存在，创建目录
    if(dir.exists() == false)
        dir.mkpath(qinfo.absolutePath());

    //记录下filetoken和文件名
    this->currentFileToken = fileToken;
    this->localFileName = localFullName;
    this->m_isDownload = true;

    //如果还未准备好，则需要在0x10回复中再开始下载
    if(this->m_isReady)
        this->startDownload();
}

//上传文件
void FiletransferHelper::uploadFile(QString fileToken, QString localFullName)
{
    this->m_isbusy = true;

    //记录下filetoken和文件名
    this->currentFileToken = fileToken;
    this->localFileName = localFullName;
    this->m_isDownload = false;

    //如果还未准备好，则需要在0x10回复中再开始上传
    if(this->m_isReady)
        this->startUpload();
}

//当前是否空闲
bool FiletransferHelper::isBusy()
{
    return this->m_isbusy;
}

//递归获取文件和文件夹
QList<QFileInfo> FiletransferHelper::recursionFiles(QFileInfo parent, bool fileOnly)
{
    QList<QFileInfo> res;
    if(parent.isDir())
    {
        //查找子目录和子文件夹
        QDir dir(parent.absoluteFilePath());
        QFileInfoList allFiles = dir.entryInfoList(QDir::Filter::Dirs | QDir::Filter::Files);

        if(allFiles.isEmpty() == false)
        {
            for(int i=0; i<allFiles.length(); i++)
            {
                QFileInfo fi = allFiles[i];

                if(fi.fileName() == "." || fi.fileName() == "..")
                    continue;

                if(fi.isDir())
                {
                    //如果是目录，递归
                    if(fileOnly == false)
                        res.append(fi);

                    QList<QFileInfo> subFiles = recursionFiles(fi, fileOnly);
                    if(subFiles.isEmpty() == false && subFiles.length() > 0)
                        res.append(subFiles);
                }
                else
                {
                    //如果是文件，加入结果集
                    res.append(fi);
                }
            }
        }
    }
    else
        res.append(parent);

    return res;
}

//开始下载
void FiletransferHelper::startDownload()
{
    //创建文件流
    this->m_file.setFileName(this->localFileName);

    if(this->m_file.open(QIODevice::WriteOnly))
    {
        //向服务器发送对应的FileGUID
        QByteArray data;
        data.append(0x11);
        data.append(this->currentFileToken.toUtf8());
        this->m_wsocket->sendBinaryMessage(data);
    }
    else
    {
        //失败，直接触发文件下载完成信号
        this->throwError();
    }

    this->m_isbusy = false;
}

void FiletransferHelper::startUpload()
{
    this->m_isbusy = true;

    if(this->m_file.isOpen())
        this->m_file.close();

    //重新打开文件
    this->m_file.setFileName(this->localFileName);
    if(this->m_file.open(QIODevice::ReadOnly) == false)
        this->throwError();

    //发送0x21
    QByteArray data;
    data.append(0x21);
    data.append(this->currentFileToken.toUtf8());
    this->m_wsocket->sendBinaryMessage(data);
}

void FiletransferHelper::throwError()
{
    if(this->m_file.isOpen())
    {
        this->m_file.flush();
        this->m_file.close();
    }
    this->m_isbusy = false;
    emit this->onTransferComplete(this, false);
}

void FiletransferHelper::slotConnected()
{
    this->m_isConnected = true;

    //上传transferToken
    QByteArray data;
    data.append(0x10);
    data.append(this->transferToken.toUtf8());
    this->m_wsocket->sendBinaryMessage(data);
}

void FiletransferHelper::slotDisconnected()
{
    this->m_isConnected = false;

    //暂时不支持断线重连，这个后续再做
    this->m_isbusy = false;
}

void FiletransferHelper::slotBinaryDataReceived(QByteArray data)
{
    if(data[0] == 0x10)
    {
        //表示服务端已经准备好传输文件了
        this->m_isReady = true;

        if(this->currentFileToken.isEmpty() == false || this->currentFileToken.isNull() == false
                || this->localFileName.isEmpty() == false || this->localFileName.isNull() == false)
        {
            //如果文件token和文件名都不为空，说明在收到服务器准备完成消息之前，外部已经调用了该类的下载或上传方法，因此需要马上开始上传或下载文件
            if(this->m_isDownload)
                this->startDownload();
            else
                this->startUpload();
        }
    }
    else if(data[0] == 0x12)
    {
        //收到文件块
        QByteArray bpos = data.mid(1,8);
        std::reverse(bpos.begin(), bpos.end());
        bool ok = false;
        long pos = bpos.toHex().toLong(&ok, 16);
        this->m_file.seek(pos);
        this->m_file.write(data.mid(9));

        int progress = (pos + data.length() - 9) * 100 / this->fileLength;
        if(this->m_progress != progress)
        {
            this->m_progress = progress;
            emit this->onTraning(this, this->m_progress);
        }

        //回应0x13
        QByteArray resp;
        resp.append(0x13);
        resp.append(this->currentFileToken.toUtf8());
        this->m_wsocket->sendBinaryMessage(resp);
    }
    else if(data[0] == 0x19)
    {
        //文件接收结束
        this->m_file.flush();
        this->m_file.close();
        this->m_isbusy = false;

        //文件传输结束信号
        emit this->onTransferComplete(this, true);
    }
    else if(data[0] == 0x22)
    {
        //收到服务器“文件块请求”
        QByteArray bfileGuid = this->currentFileToken.toUtf8();

        if(this->m_file.pos() <= this->m_file.size() - 1)
        {
            QByteArray fdata;
            fdata.append(0x23);
            fdata.append(bfileGuid);

            //文件位置
            QBuffer buff;
            buff.open(QBuffer::ReadWrite);
            QDataStream stream(&buff);
            stream<<this->m_file.pos();
            QByteArray bpos = buff.buffer();
            fdata.append(bpos);

            QByteArray arr = this->m_file.read(this->m_block);
            fdata.append(arr);
            this->m_wsocket->sendBinaryMessage(fdata);

            int progress = this->m_file.pos() * 100 / this->m_file.size();
            if(progress != this->m_progress)
            {
                this->m_progress = progress;
                emit onTraning(this, this->m_progress);
            }
        }
        else
        {
            //文件传输完成，发送结束符
            QByteArray endSignal;
            endSignal.append(0x29);
            endSignal.append(bfileGuid);
            this->m_wsocket->sendBinaryMessage(endSignal);
        }
    }
    else if(data[0] == 0x29)
    {
        //文件上传完成
        this->m_isbusy = false;

        //文件传输结束信号
        emit this->onTransferComplete(this, true);
    }
    else if(data[0] == 0x31)
    {
        this->throwError();
    }
}

void FiletransferHelper::slotError(QAbstractSocket::SocketError)
{

}
